package main

import (
	"fmt"
	"strings"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
	"encoding/json"
	"strconv"
	"github.com/hyperledger/fabric/core/chaincode/lib/cid"
	"crypto/x509"
	"encoding/base64"
	"crypto/sha256"
	"encoding/hex"
)

type cratorinfo struct {
	ID   string
	ORG  string
	CERT *x509.Certificate
}

type supplycc struct {}

func (t *supplycc) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Init success! ")
	return shim.Success([]byte("Init success !!!!!"))
}

func (t *supplycc) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()
	switch function {
	case "putvalue":
		if len(args) != 2 {
			return shim.Error("putvalue params num err!")
		}
		return t.putvalue(stub, args[0], args[1])
	case "putvaluesend": // 发货
		if len(args) != 2 {
			return shim.Error("putvalue params num err!")
		}
		return t.putvaluesend(stub, args[0], args[1])
	case "putvaluerecv": // 确认
		if len(args) != 2 {
			return shim.Error("putvalue params num err!")
		}
		return t.putvaluerecv(stub, args[0], args[1])
	case "putvaluerecv1": // 收货
		if len(args) != 2 {
			return shim.Error("putvalue params num err!")
		}
		return t.putvaluerecv1(stub, args[0], args[1])
	case "getlastvalue":
		if len(args) != 1 {
			return shim.Error("getlastvalue params num err!")
		}
		return t.getlastvalue(stub,args[0])
	case "gethistory":
		if len(args) != 1 {
			return shim.Error("gethistory params num err!")
		}
		return t.gethistory(stub,args[0])
	case "setpicture": 
		return t.setpicture(stub,args[0])
	case "getpicture": 
		return t.getpicture(stub,args[0])
	case "creator": //返回调用者信息
		return t.creator(stub,args)
	case "creator2"://返回调用者信息,方法2
		return t.creator2(stub,args)
	case "getattr"://获取用户的属性
	    return t.getattr(stub,args)
	case "setquota"://给公司设置贷款最大额度
		return t.setquota(stub,args)
	case "checkquota"://查看现在还剩多少信用额度
	    return t.checkquota(stub,args[0])
	case "loan"://执行贷款逻辑
	    return t.loan(stub,args)
	case "requestloan"://企业申请贷款
		return t.requestloan(stub,args)
	default:
		return shim.Error("Invalid invoke function name.")
	}
}

//申请贷款 args: 0:项目（公司）名  1:贷款时长  2:贷款额度
func (t *supplycc) requestloan(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 3 {
		return shim.Error("setquota params num err!")
	}
	projectName := args[0]
	loanTime := args[1]
	loanNum := args[2]
	loanItem := &LoanItem{
		ProjectName:projectName,
		LoanTime:loanTime,
		LoanNum:loanNum,
	}

	loanItemStr,e := json.Marshal(loanItem)
	if e != nil {
		return shim.Error("requestloan error, json Marshal error")
	}
	resp1 := t.putvaluesend(stub,"query_loan_"+projectName,string(loanItemStr))
	if resp1.Status != shim.OK {
		return shim.Error("setquota fail ")
	}
	return shim.Success([]byte("success requestloan: " + string(loanItemStr)))
}


//设置公司或者项目贷款配额
func (t *supplycc) setquota(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 2 {
		return shim.Error("setquota params num err!")
	}
	key := args[0]
	value := args[1]
	fmt.Printf("setquota %s, value is %s\n", key, value)
	bytes, err := stub.GetState(key)
	if err != nil {
		return shim.Error("query fail " + err.Error())
	}
	if string(bytes) != "" {
		return shim.Success([]byte("key: " + key + " has been seted quota and the remaining quota is : " + string(bytes)))
	}

	resp1 := t.putvalue(stub,key,value)
	if resp1.Status != shim.OK {
		return shim.Error("setquota fail ")
	}
	return shim.Success([]byte("success setquota key: " + key + ", value: " + value))
}

//查询quota剩余量，key为项目名或者公司名
func (t *supplycc) checkquota(stub shim.ChaincodeStubInterface, key string) pb.Response {
	fmt.Printf("checkquota %s\n", key)
	resp := t.getlastvalue(stub,key)
	if resp.Status != shim.OK {
		return shim.Error("checkquota fail ")
	}
	return shim.Success(resp.Payload)
}

//银行放款给企业 args: 0:项目名 1:合同ID 2:贷款时长 3:贷款额度
type LoanItem struct {
	ProjectName string
	LoanID  string
	LoanTime string
	LoanNum string
}

func (t *supplycc) loan(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 4 {
		return shim.Error("loan params number error")
	}
	projectName := args[0]
	LoanID := args[1]
	loanTime := args[2]
	loanNum := args[3]
	loanItem := &LoanItem{
		ProjectName:projectName,
		LoanTime:loanTime,
		LoanID:LoanID,
		LoanNum:loanNum,
	}

	//检查是否申请过贷款
	// req := t.getlastvalue(stub, "query_loan_" + projectName)
	// if req.Status != shim.OK {
	// 	return shim.Error("please request loan for project first.")
	// }

	//订单状态的校验
	orderstatus := t.getlastvalue(stub, "order")
	if orderstatus.Status != shim.OK {
		return shim.Error("query order status failed.")
	}

	if string(orderstatus.Payload) != "3" {
		return shim.Error("order status is wrong.")
	}
	
	//先查看是否有额度
	qutaRemaining := t.checkquota(stub,projectName)
	if qutaRemaining.Status != shim.OK{
		return shim.Error("loan error")
	}
	quta, err1 := strconv.ParseInt(string(qutaRemaining.Payload), 10, 64)
	loanN, err2 := strconv.ParseInt(loanNum, 10, 64)
	if err1 != nil || err2 != nil {
		return shim.Error("loan error, type error")
	}
	//贷款额度超过配额
	if loanN - quta > 0 {
		return shim.Success([]byte("quta is not enough, can loan " + string(qutaRemaining.Payload) + " at most"))
	}

	resp1 := t.putvalue(stub,"loan_" + projectName,loanNum)
	if resp1.Status != shim.OK {
		return shim.Error("loan putvalue fail ")
	}
	remainingquta := strconv.FormatInt(quta-loanN,10)
	resp2 := t.putvalue(stub,projectName,remainingquta)
	if resp2.Status != shim.OK {
		return shim.Error("loan putvalue fail ")
	}

	resp3 := t.putvalue(stub, "order", "4")
	if resp3.Status != shim.OK {
		return shim.Error("change order status fail ")
	}

	loanItemStr,e := json.Marshal(loanItem)
	if e != nil {
		return shim.Error("loan error, json Marshal error")
	}

	return shim.Success(loanItemStr)
}

func(t *supplycc) getidandcheck(stub shim.ChaincodeStubInterface, user string) pb.Response{
	cinfo := cratorinfo{}
	cinfostr := t.creator2(stub, []string{user})
	fmt.Println("cinfostr:", string(cinfostr.Payload))

	err := json.Unmarshal(cinfostr.Payload, &cinfo)
	if err != nil {
		return shim.Error("putvalue get creator id fail " + err.Error())
	}
	fmt.Println("cinfo ID ===:", cinfo.ID)

	if !strings.Contains(cinfo.ID, user) {
		fmt.Println("getidandcheck, sorry permission denied")
		return shim.Error("getidandcheck, sorry permission denied")
	}

	return shim.Success([]byte(cinfo.ID))
}

func (t *supplycc) setpicture(stub shim.ChaincodeStubInterface, value string) pb.Response {
	
	fmt.Printf("setpicture value is %s\n", value)
	if err := stub.PutState("picture", []byte(value)); err != nil {
		return shim.Error("setpicture fail " + err.Error())
	}
	return shim.Success([]byte("success setpicture"))
}

func (t *supplycc) getpicture(stub shim.ChaincodeStubInterface, key string) pb.Response {
	
	fmt.Printf("getpicture key is %s\n", key)
	res := t.getlastvalue(stub, "picture")
	if res.Status != shim.OK {
		return shim.Error("getpicture fail ")
	}

	return shim.Success(res.Payload)
}

func (t *supplycc) putvalue(stub shim.ChaincodeStubInterface, key, value string) pb.Response {

	// 校验金融机构的身份
	res := t.getidandcheck(stub, "Admin@org1.example.com")
	if res.Status != shim.OK {
		return shim.Error("sorry, permission denied.")
	}
	
	fmt.Printf("putvalue %s, value is %s\n", key, value)
	if err := stub.PutState(key, []byte(value)); err != nil {
		return shim.Error("putvalue fail " + err.Error())
	}
	return shim.Success([]byte("success put key: " + key + ", value: " + value))
}

func (t *supplycc) putvaluesend(stub shim.ChaincodeStubInterface, key, value string) pb.Response {

	// 校验供应商的身份
	res := t.getidandcheck(stub, "User1@org1.example.com")
	if res.Status != shim.OK {
		return shim.Error("sorry, permission denied.")
	}

	fmt.Printf("putvalue %s, value is %s\n", key, value)
	if err := stub.PutState(key, []byte(value)); err != nil {
		return shim.Error("putvalue fail " + err.Error())
	}
	return shim.Success([]byte("success put key: " + key + ", value: " + value))
}

func (t *supplycc) putvaluerecv(stub shim.ChaincodeStubInterface, key, value string) pb.Response {

	res := t.getidandcheck(stub, "User2@org1.example.com")
	if res.Status != shim.OK {
		return shim.Error("sorry, permission denied.")
	}

	fmt.Printf("putvalue %s, value is %s\n", key, value)
	if err := stub.PutState(key, []byte(value)); err != nil {
		return shim.Error("putvalue fail " + err.Error())
	}
	return shim.Success([]byte("success put key: " + key + ", value: " + value))
}

func (t *supplycc) putvaluerecv1(stub shim.ChaincodeStubInterface, key, value string) pb.Response {

	res := t.getidandcheck(stub, "User3@org1.example.com")
	if res.Status != shim.OK {
		return shim.Error("sorry, permission denied.")
	}

	fmt.Printf("putvalue %s, value is %s\n", key, value)
	if err := stub.PutState(key, []byte(value)); err != nil {
		return shim.Error("putvalue fail " + err.Error())
	}
	return shim.Success([]byte("success put key: " + key + ", value: " + value))
}

func (t *supplycc) gethistory(stub shim.ChaincodeStubInterface, key string) pb.Response {
	fmt.Printf("history %s\n", key)
	iter, err := stub.GetHistoryForKey(key)
	defer iter.Close()
	if err != nil {
		return shim.Error("query fail " + err.Error())
	}

	values := make(map[string]string)

	for iter.HasNext() {
		fmt.Printf("next\n")
		if kv, err := iter.Next(); err == nil {
			fmt.Printf("id: %s value: %s\n", kv.TxId, kv.Value)
			values[kv.TxId] = string(kv.Value)
		}
		if err != nil {
			return shim.Error("iterator history fail: " + err.Error())
		}
	}

	bytes, err := json.Marshal(values)
	if err != nil {
		return shim.Error("json marshal fail: " + err.Error())
	}

	// 校验金融机构的身份
	res := t.getidandcheck(stub, "Admin@org1.example.com")
	if res.Status != shim.OK {
		h := sha256.New()
    	h.Write(bytes)
		return shim.Success([]byte(hex.EncodeToString(h.Sum(nil))))
	}

	return shim.Success(bytes)
}

func (t *supplycc) getlastvalue(stub shim.ChaincodeStubInterface, key string) pb.Response {
	fmt.Printf("query %s\n", key)
	bytes, err := stub.GetState(key)
	if err != nil {
		return shim.Error("query fail " + err.Error())
	}
	return shim.Success(bytes)
}

func (t *supplycc) getattr(stub shim.ChaincodeStubInterface, args []string) pb.Response{
	if len(args) != 1 {
		return shim.Error("parametes's number is wrong")
	}
	fmt.Println("get attr: ", args[0])
	value, ok, err := cid.GetAttributeValue(stub, args[0])
	if err != nil {
		return shim.Error("get attr error: " + err.Error())
	}

	if ok == false {
		value = "not found"
	}
	bytes, err := json.Marshal(value)
	if err != nil {
		return shim.Error("json marshal error: " + err.Error())
	}
	return shim.Success(bytes)
}

func (t *supplycc) creator(stub shim.ChaincodeStubInterface, args []string) pb.Response{
	fmt.Println("creator: ", args)
	bytes, err := stub.GetCreator()
	if err != nil {
		return shim.Error("get creator error: " + err.Error())
	}

	return shim.Success(bytes)
}
func (t *supplycc) creator2(stub shim.ChaincodeStubInterface, args []string) pb.Response{
	cinfo := cratorinfo{}

	fmt.Println("creator2: ", args)

	id, err := cid.GetID(stub)
	if err != nil {
		return shim.Error("getid error: " + err.Error())
	}

	id_readable, err := base64.StdEncoding.DecodeString(id)
	if err != nil {
		return shim.Error("base64 decode error: " + err.Error())
	}
	cinfo.ID = string(id_readable)
	fmt.Println("cinfo.ID:", cinfo.ID)

	mspid, err := cid.GetMSPID(stub)
	if err != nil {
		return shim.Error("getmspid error: " + err.Error())
	}
	cinfo.ORG = mspid
	fmt.Println("cinfo.ORG", cinfo.ORG)

	cert, err := cid.GetX509Certificate(stub)
	if err != nil {
		return shim.Error("getX509Cert error: " + err.Error())
	}
	cinfo.CERT = cert

	bytes, err := json.Marshal(cinfo)
	if err != nil {
		return shim.Error("json marshal error: " + err.Error())
	}

	fmt.Println("bytes", string(bytes))

	return shim.Success(bytes)
}

func main() {
	err := shim.Start(new(supplycc))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}